package com.atguigu.exam.service.impl;


import com.atguigu.exam.common.CacheConstants;
import com.atguigu.exam.entity.PaperQuestion;
import com.atguigu.exam.entity.Question;
import com.atguigu.exam.entity.QuestionAnswer;
import com.atguigu.exam.entity.QuestionChoice;
import com.atguigu.exam.mapper.PaperQuestionMapper;
import com.atguigu.exam.mapper.QuestionAnswerMapper;
import com.atguigu.exam.mapper.QuestionChoiceMapper;
import com.atguigu.exam.mapper.QuestionMapper;
import com.atguigu.exam.service.QuestionService;
import com.atguigu.exam.utils.ExcelUtil;
import com.atguigu.exam.utils.RedisUtils;
import com.atguigu.exam.vo.QuestionImportVo;
import com.atguigu.exam.vo.QuestionPageVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 题目Service实现类
 * 实现题目相关的业务逻辑
 */
@Slf4j
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {
    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private QuestionAnswerMapper questionAnswerMapper;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private QuestionChoiceMapper questionChoiceMapper;

    @Autowired
    private PaperQuestionMapper paperQuestionMapper;

    @Override
    public void queryPage(Page<Question> questionPage, QuestionPageVo questionQueryVo) {
        questionMapper.customPage(questionPage, questionQueryVo);
    }

    @Override
    public void customPageJavaService(Page<Question> pageBean, QuestionPageVo questionPageVo) {
        //分页查询题目列表（多条件）
        LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(!ObjectUtils.isEmpty(questionPageVo.getType()), Question::getType, questionPageVo.getType());
        lambdaQueryWrapper.eq(!ObjectUtils.isEmpty(questionPageVo.getDifficulty()), Question::getDifficulty, questionPageVo.getDifficulty());
        lambdaQueryWrapper.eq(!ObjectUtils.isEmpty(questionPageVo.getCategoryId()), Question::getCategoryId, questionPageVo.getCategoryId());
        lambdaQueryWrapper.like(!ObjectUtils.isEmpty(questionPageVo.getKeyword()), Question::getTitle, questionPageVo.getKeyword()).
                like(!ObjectUtils.isEmpty(questionPageVo.getKeyword()), Question::getAnalysis, questionPageVo.getKeyword());
        //时间的倒叙排序
        lambdaQueryWrapper.orderByDesc(Question::getCreateTime);
        page(pageBean, lambdaQueryWrapper);
        //提取一个方法，给题目进行选项和答案
        fillQuestionChoiceAndAnswer(pageBean.getRecords());

    }

    @Override
    public Question customDetailQuestion(Long id) {
        //1 查询题目信息
        Question question = questionMapper.customGetById(id);
        if (question == null) {
            throw new RuntimeException("没有查询到对应的题目信息!!");
        }
        //进行热点题目缓存
        new Thread(() -> {
            incrementQuestion(question.getId());
        }).start();
        return question;
    }


    /**
     * 自定义保存题目方法
     * 用于保存题目信息， 包括题目基本信息， 答案和选项（选择题）
     * 业务逻辑说明：
     * 1. 题目去重检查：同一类型下不允许存在相同标题的题目
     * 2. 题目保存：将题目基本信息保存到数据库
     * 3. 答案处理：保存题目的答案信息
     * 4. 选项处理：针对选择题，保存选项并生成标准答案
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void customSaveQuestion(Question question) {
        //插入题目信息
        LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Question::getType, question.getType());
        queryWrapper.eq(Question::getTitle, question.getTitle());
        boolean exists = baseMapper.exists(queryWrapper);
        if (exists) {
            //同一类型，title相同
            throw new RuntimeException("在%s下，存在%s 名称的题目已经存在！保存失败！".formatted(question.getType(), question.getTitle()));
        }
        boolean saved = save(question);
        if (!saved) {
            //同一类型，title相同
            throw new RuntimeException("在%s下，存在%s 名称的题目！保存失败！".formatted(question.getType(), question.getTitle()));
        }
        //获取答案
        QuestionAnswer answer = question.getAnswer();
        answer.setQuestionId(question.getId());
        //判断是不是选择题
        if ("CHOICE".equals(question.getType())) ;
        List<QuestionChoice> choices = question.getChoices();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < choices.size(); i++) {
            QuestionChoice Choice = choices.get(i);
            //确保正确顺序
            Choice.setSort(i);
            Choice.setQuestionId(question.getId());
            questionChoiceMapper.insert(Choice);
            if (Choice.getIsCorrect()) {
                if (sb.length() > 0) {
                    sb.append(",");
                }
                sb.append((char) ('A' + i));
            }
        }
        answer.setAnswer(sb.toString());

        // 4.保存答案对象
        questionAnswerMapper.insert(answer);
        // 5.保证方法的一致性！ 需要添加事务
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void customUpdateQuestion(Question question) {
        //题目的校验
        LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Question::getTitle, question.getTitle());
        queryWrapper.eq(Question::getId, question.getId());
        boolean exists = baseMapper.exists(queryWrapper);
        if (exists) {
            throw new RuntimeException("修改：%s题目的新标题：%s和其他的题目重复了！修改失败！".formatted(question.getId(), question.getTitle()));
        }
        //修改题目
        boolean updated = updateById(question);
        if (!updated) {
            throw new RuntimeException("修改：%s题目失败！！".formatted(question.getId()));
        }
        //获取答案对象
        QuestionAnswer answer = question.getAnswer();
        // 判断是否是选择题
        if ("CHOICE".equals(question.getType())) {
            List<QuestionChoice> choiceList = question.getChoices();
            //删除题目对象的所有选择 (根据题目id 删除)
            LambdaQueryWrapper<QuestionChoice> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(QuestionChoice::getQuestionId, question.getId());
            questionChoiceMapper.delete(lambdaQueryWrapper);
            //循环新增选项
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < choiceList.size(); i++) {
                QuestionChoice choice = choiceList.get(i);
                choice.setId(null);
                //确保，正确顺序！
                choice.setSort(i);
                choice.setCreateTime(null);
                choice.setUpdateTime(null);
                //新增选择需要
                choice.setQuestionId(question.getId());
                questionChoiceMapper.insert(choice);
                if (choice.getIsCorrect()) {
                    if (sb.length() > 0) {
                        sb.append(",");
                    }
                    sb.append((char) ('A' + i));
                }
                //答案对象赋值选择题答案
                answer.setAnswer(sb.toString());
            }
            // 答案的修改
            questionAnswerMapper.updateById(answer);
        }
    }

    @Override
    public void customRemoveQuestionBuId(Long id) {
        //判断试卷提目表中是否有该题目
        LambdaQueryWrapper<PaperQuestion> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(PaperQuestion::getQuestionId, id);
        Long count = paperQuestionMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new RuntimeException("题目有试卷引用，删除失败！");

        }
        //删除题目表
        boolean remove = removeById(id);
        if (!remove) {
            throw new RuntimeException("删除题目失败！");
        }
        //删除子表 答案 和选项表
        questionAnswerMapper.delete(new LambdaQueryWrapper<QuestionAnswer>().eq(QuestionAnswer::getQuestionId, id));
        questionChoiceMapper.delete(new LambdaQueryWrapper<QuestionChoice>().eq(QuestionChoice::getQuestionId, id));
    }

    @Override
    public List<Question> customFindPopularQuestions(Integer size) {
        // 定义热门题目集合
        ArrayList<Question> popularQuestions = new ArrayList<>();
        //去zset 中获取热门题目， 并且 添加到总集合中
        // 获取题目排行，需要获取id和分数！ 分数用于后续的排序处理！
        Set<ZSetOperations.TypedTuple<Object>> tupleSet = redisUtils.zReverseRangeWithScores(CacheConstants.POPULAR_QUESTIONS_KEY, 0, size - 1);
        //定义接收id的集合
        List<Long> idsSet = new ArrayList<>();
        if (tupleSet != null && tupleSet.size() > 0) {
            //根据排行榜的积分，倒序进行Id查询！
            List<Long> idsList = tupleSet.stream().sorted((o1, o2) -> Integer.compare(o2.getScore().intValue(), o1.getScore().intValue()))
                    .map(o -> Long.valueOf(o.getValue().toString())).collect(Collectors.toList());
            //复制，用于后面补充！！
            idsSet.addAll(idsList);
            log.debug("从redis获取热门题目的id集合，且保证顺序：{}", idsList);
            for (Long id : idsList) {
                Question question = getById(id);
                //防止redis 有缓存，但是数据库中没有数据！ 后续优化，删除题目， 应该删除热门题目榜单中对应的value
                if (question != null) {
                    popularQuestions.add(question);
                }
            }
            log.debug("去redis查询的热门题目，题目数：{},题目内容为：{}", popularQuestions.size(), popularQuestions);
        }
        int diff = size - popularQuestions.size();
        if (diff > 0) {
            LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.notIn(Question::getId, idsSet);
            lambdaQueryWrapper.orderByDesc(Question::getCreateTime);

            lambdaQueryWrapper.last("limit " + diff);
            List<Question> questionDiffList = list(lambdaQueryWrapper);
            log.debug("去question表中补充热门题目，题目数：{},题目内容为：{}", questionDiffList.size(), questionDiffList);
            if (questionDiffList != null && questionDiffList.size() > 0) {
                popularQuestions.addAll(questionDiffList);

            }
        }
        fillQuestionChoiceAndAnswer(popularQuestions);
        return popularQuestions;
    }

    //预览
    @Override
    public List<QuestionImportVo> previewData(MultipartFile file) throws IOException {
        //数据校验
        if (file == null || file.isEmpty()) {
            throw new RuntimeException("上传失败！原因：上传的文件不能为空！");
        }
        String fileName = file.getOriginalFilename();
        if (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls")) {
            throw new RuntimeException("请上传Excel文件（.xlsx或.xls格式）");
        }
        //解析数据
        List<QuestionImportVo> questionImportVoList = ExcelUtil.parseExcel(file);
        //返回结构
        return questionImportVoList;
    }

    //进行题目批量导入
    @Override
    public int importBatchQuestions(List<QuestionImportVo> questions) {
        //进行数据校验
        if (questions == null || questions.isEmpty()) {
            throw new RuntimeException("导入失败！原因：导入的题目数据不能为空！");
        }
        //循环+try 调用保存的方法
        int succerCount = 0;
        for (int i = 0; i < questions.size(); i++) {
            try {
                Question question = convertQuestipnImportvotoQuestion(questions.get(i));
                //数据单体保存
                customSaveQuestion(question);
                //累计分数
                succerCount++;
            } catch (Exception e) {
                //导入失败提示
                log.debug("导入失败，题目ID: {}, 错误: {}", questions.get(i), e.getMessage());

            }

        }
        return succerCount;
    }

    @Override
    public String importExeclBatchQuestions(MultipartFile file) throws IOException {
        //校验
        if (file == null || file.isEmpty()){
            throw new RuntimeException("导入失败！原因：上传的文件不能为空！");
        }
        String fileName = file.getOriginalFilename();
        if (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xlsx")){
            throw new RuntimeException("请上传Excel文件（.xlsx或.xls格式）");
        }
        //解析文件
        List<QuestionImportVo> questionImportVoList = ExcelUtil.parseExcel(file);
        //调用批量导入业务
        int succerCount = importBatchQuestions(questionImportVoList);
        String result = "批量导入题目接口调用成功！ 一共：%s 题目需要导入，成功导入了：%s 道题！".formatted(questionImportVoList.size(), succerCount);
        return result;
    }

    private Question convertQuestipnImportvotoQuestion(QuestionImportVo questionImportVo) {
        //给question赋值
        Question question = new Question();
        //作用 给对象的属性进行赋值！根据接受一个对象的相同属性值！
        BeanUtils.copyProperties(questionImportVo, question);
        // 判断是选择， 给选择集合进行赋值
        if ("CHOICE".equals(questionImportVo.getType())) {
            if (questionImportVo.getChoices() != null && !questionImportVo.getChoices().isEmpty()) {
                List<QuestionChoice> questionChoices = new ArrayList<>(questionImportVo.getChoices().size());
                for (QuestionImportVo.ChoiceImportDto importVoChoice : questionImportVo.getChoices()) {
                    QuestionChoice questionChoice = new QuestionChoice();
                    questionChoice.setContent(importVoChoice.getContent());
                    questionChoice.setIsCorrect(importVoChoice.getIsCorrect());
                    questionChoice.setSort(importVoChoice.getSort());
                    questionChoices.add(questionChoice);
                }
                question.setChoices(questionChoices);
            }
        }
        QuestionAnswer questionAnswer = new QuestionAnswer();

//        //判断题，需要将true和false转成大写！ 否则无法识别！！
//        if ("JUDGE".equals(questionImportVo.getType())){
//            questionAnswer.setAnswer(questionImportVo.getAnswer().toUpperCase());
//        }else{
//            questionAnswer.setAnswer(questionImportVo.getAnswer());
//        }

        if ("CHOICE".equals(questionImportVo.getType())) {
            // 对于选择题，答案可能需要从选项中生成
            questionAnswer.setAnswer(questionImportVo.getAnswer() != null ?
                questionImportVo.getAnswer().toUpperCase() : "");
        } else {
            // 对于判断题和简答题，直接使用导入的答案
            questionAnswer.setAnswer(questionImportVo.getAnswer() != null ?
                questionImportVo.getAnswer() : "");
        }
        // 设置答案关键词
        questionAnswer.setKeywords(questionImportVo.getKeywords());
        // 将答案对象设置到题目中
        question.setAnswer(questionAnswer);
        return question;
    }

    //异步方法
    private void incrementQuestion(Long questionId) {
        Double score = redisUtils.zIncrementScore(CacheConstants.POPULAR_QUESTIONS_KEY, questionId, 1);
        log.info("完成{}题目分数累计，累计后分数为：{}", questionId, score);
    }


    private void fillQuestionChoiceAndAnswer(List<Question> questionList) {
        //1 非空判断
        if (questionList == null || questionList.size() == 0) {
            log.debug("没有查询对应的问题集合数据!!");
            return;
        }
        //2 查询所有答案和选项
        List<Long> ids = questionList.stream().map(Question::getId).collect(Collectors.toList());
        //查询本次题目的选项集合
        List<QuestionChoice> questionChoiceList =
                questionChoiceMapper.selectList(new LambdaQueryWrapper<QuestionChoice>()
                        .in(QuestionChoice::getQuestionId, ids)
                );
        // 查询本次题目的答案集合
        List<QuestionAnswer> questionAnswers = questionAnswerMapper.selectList(
                new LambdaQueryWrapper<QuestionAnswer>()
                        .in(QuestionAnswer::getQuestionId, ids)
        );

        //答案和选项进行map 转换
        Map<Long, List<QuestionChoice>> questionChoiceMap =
                questionChoiceList.stream().collect(Collectors
                        .groupingBy(QuestionChoice::getQuestionId));

        Map<Long, QuestionAnswer> answerMap =
                questionAnswers.stream().collect(Collectors
                        .toMap(QuestionAnswer::getQuestionId, a -> a));
//循环问题集合，给问题设置选项和答案
        questionList.forEach(question -> {
            question.setAnswer(answerMap.get(question.getId()));
            if ("CHOICE".equals(question.getType())) {
                List<QuestionChoice> questionChoices = questionChoiceMap.get(question.getId());
                questionChoices.sort(Comparator.comparingInt(QuestionChoice::getSort));
                question.setChoices(questionChoices);
            }
        });
    }
}